-- Grid
-- Create by panyl
-- 地宫格子对象

require "game/logic/base/Dungeon"
require "game/logic/base/DungeonInstance"
require "game/logic/base/Monster"

Grid = {
    class = 0,
    state = GRID_STATE_COVER,
    x = 0,
    y = 0,
    type   = GRID_TYPE_EMPTY,
    hasKey = 0,
};
Grid.__index = Grid;

-- 构造函数
function Grid.new(class, x, y, state, info)
    local self = {};
    setmetatable(self, Grid);

    self.class = class
    self.state = state
    self.x = x
    self.y = y
    self.type = DungeonM.query(class, "type")

    for key, v in pairs(info) do
        self[key] = v
    end

    -- 怪物
    if self.type == GRID_TYPE_MONSTER or self.type == GRID_TYPE_BOSS or self.type == GRID_TYPE_BOX then
        -- 创建一个怪物
        info["owner"] = self:getPos();

        local classId;

        if type(self.class_id) == "number" then
            classId = self.class_id;
        elseif type(self.class_id) == "table" then
            local arr = self.class_id;
            local rand = DungeonM.getRandSeed("random box monster");
            table.sort(arr);

            classId = arr[rand % #arr + 1];
        elseif self.class_id == "arena_slime_id" then
            -- 角斗台冈布奥
            classId = ArenaM.getCombatSlimeId();
        elseif self.class_id == "arena_audience_id"  then
            -- 角斗台观众
            classId = ArenaM.takeAudienceId();
        else
            assert(false, "暂不支持的class_id配置");
        end

        if UnrealDungeonM.isInUnreal() and not ActivityDungeonM.isInActivityBossLayer() then
            -- 是在神龙夹层中
            local unrealId = UnrealDungeonM.getUnrealId();
            local dbase = UnrealDungeonM.query(unrealId, "dbase");

            local maxDungeonLayer = ME.user.dbase:query("max_dungeon_layer");
            if type(dbase["monster_layer_fix"]) == "number" then
                -- 配置了层数，按照传入的层数计算
                info["layer"] = dbase["monster_layer_fix"];
            elseif type(maxDungeonLayer) == "number" and not ArenaM.isInArena() then
                -- 不在角斗台中，默认按照最高层数计算
                info["layer"] = maxDungeonLayer;
            end

            self.monster = MonsterM.createDungeonMonster(classId, info);

            if ArenaM.isInArena() then
                -- 在角斗台中，通过角斗台模块计算掉落
                local bonus = ArenaM.calcDrop(self.monster);
                if type(bonus) == "table" and #bonus > 0 then
                    self.bonus = bonus;
                else
                    self.bonus = nil;
                end
            elseif type(dbase) ~= 'table' or dbase.can_drop ~= 1 then
                -- 没有配置掉落，需要把bonus删除掉
                self.bonus = nil;
            end
        elseif ActivityDungeonM.isInActivityDungeon() then
            -- 活动副本
            self.monster = MonsterM.createDungeonMonster(classId, info);

            if self.monster:isAudience() then
                -- 一般观众模块抽取
                local bonus = NormalAudienceM.calcDrop(self.monster);
                if type(bonus) == "table" and #bonus > 0 then
                    self.bonus = bonus;
                else
                    self.bonus = nil;
                end
            elseif ActivityDungeonM.isInActivityBossLayer() then
                -- 活动副本BOSS层
                local bonus = ActivityDungeonM.calcDrop(self.monster);
                if type(bonus) == "table" and #bonus > 0 then
                    self.bonus = bonus;
                end
            end
        else
            self.monster = MonsterM.createDungeonMonster(classId, info);
        end

        PropM.refresh(self.monster);

        local configDbase = MonsterM.query(classId, "dbase") or {};
        if type(configDbase["create_stat"]) == "string" then
            -- 创建需要统计
            BuildingBonusM.addFieldStat(configDbase["create_stat"], 1);
        end
    elseif self.type == GRID_TYPE_SUMMON then
        -- 创建一个召唤兽
        local classId = self.class_id;
        if type(classId) == "number" then
            self.summon = SummonM.createSummon(info);
            self.summon.gridPos = self.pos;

            -- 刷出的召唤兽记录在玩家 temp 数据中，以便后续查找
            local summon_list = ME.user.dbase:queryTemp("summon_list", {});
            table.insert(summon_list, self.summon);
            ME.user.dbase:setTemp("summon_list", summon_list);

            -- 刷新属性
            PropM.refresh(self.summon);
        end
    elseif self.type == GRID_TYPE_GHOST_SHIP then
        -- 幽灵船

        local para = table.add(info, GhostShipM.fetchShipData(self.class_id));
        para["pos"] = self:getPos();

        local ship = GhostShipM.createShip(self.class_id, para);

        if ship:isHired() then
            self.ship = ship;
        else
            self.monster = ship;
        end

        PropM.refresh(ship);
    elseif self.type == GRID_TYPE_TREE_HOUSE then
        -- 树屋
        local para = table.add(info, TreeHouseM.fetchTreeHouseData(self.class_id));
        para["pos"] = self:getPos();
        -- 创建新的树屋
        local treeHouse = TreeHouseM.createTreeHouse(self.class_id, para);
        self.tree_house = treeHouse;
    elseif self.type == GRID_TYPE_BISHOP then
        -- 主教
        if not ME.user.bishop then
            local info = ME.user.dbase:query("bishop");
            if type(info) == "table" and type(info.class_id) == "number" then
                info["pos"] = self:getPos();
                -- 创建主教对象
                BishopM.createBishop(info.class_id, info);
            end
        end
    elseif self.type == GRID_TYPE_AVALON_CANNON then
        -- 阿瓦隆巨炮
        local para = table.add(info, AvalonCannonM.fetchAvalonCannonData(self.class_id));
        para["pos"] = self:getPos();
        -- 创建新的阿瓦隆巨炮
        local avalonCannon = AvalonCannonM.createAvalonCannon(self.class_id, para);
        self.avalon_cannon = avalonCannon;
    elseif self.type == GRID_TYPE_STEAM_MACHINE then
        -- 蒸汽机械

        local para = table.copy(info);
        para["pos"] = self:getPos();

        local machine = SteamMachineM.createMachine(self.class_id, para);

        self.steam_machine = machine;
    end

    return self;
end

-- 取得该对象的描述字符串
function Grid:get_ob_id()
    return string.format("Grid:%s:%s", tostring(self.pos), tostring(self.class));
end

-- 是否已经翻开
function Grid:isOpened()
    return self.state ~= GRID_STATE_COVER;
end

-- 是否能翻开
function Grid:canOpen()
    return DungeonM.canOpenGrid(self:getPos())
end

-- 开启格子
function Grid:open(isQuite, isDelay)
    Profiler.funcBegin("Grid:open");

    self:changeState(GRID_STATE_OPEN);

    -- 是否静默开启
    isQuite = isQuite or false;

    -- 是否延迟播放
    isDelay = isDelay or false;

    -- 抛出事件
    EventMgr.fire(event.OPEN_GRID, { ["type"] = self.type, ["pos"] = self:getPos(),
        ["isQuite"] = isQuite, ["isDelay"] = isDelay, ["isPublicGrid"] = self:isPublicGrid()});

    Profiler.funcEnd("Grid:open");
end

-- 开启格子（不影响周围格子局势）
function Grid:pureOpen()
    self:changeState(GRID_STATE_OPEN);

    -- 抛出事件
    EventMgr.fire(event.PURE_OPEN_GRID, { ["type"] = self.type, ["pos"] = self:getPos(), });
end

-- 获取位置
function Grid:getPos()
    return DungeonM.convertToPos(self.x, self.y);
end

-- 改变状态
function Grid:changeState(state)
    self.state = state;

    EventMgr.fire(event.CHANGE_GRID_STATE, { ["state"] = state, ["pos"] = self:getPos(), ["type"] = self.type });
end

-- 地牢数据
function Grid:query(path)
    if self.path then
        return self.path
    else
        return DungeonM.query(self.class, path);
    end
end

-- 是否远程怪物
function Grid:isRemoteMonster()
    if self.type ~= GRID_TYPE_MONSTER or not self.monster then
        return false;
    end

    -- 是否有远程属性
    if self.monster:isRemoteMonster() then
        return true;
    end

    return false;
end

-- 是否是怪物
function Grid:isMonster()
    -- 有怪物的
    local monster = self.monster;
    if monster and not monster:isDead() then
        return true;
    end

    return self.type == GRID_TYPE_MONSTER or self.type == GRID_TYPE_BOSS;
end

-- 是否公开格子
function Grid:isPublicGrid()
    -- 被消除特效的远程怪物
    if self:isMonster() and self.monster.dbase:query("remote_can_not_lock") then
        return true;
    end

    -- 还未死亡的远程怪物
    if self:isRemoteMonster() and not self.monster:isDead() then
        return true;
    end

    -- 还未拾取过的地图
    if self.type == GRID_TYPE_MAP and self.bonus then
        return true;
    end

    -- 如果有配置了“显示”
    if self.display then
        return true;
    end

    -- 其他的暂时不公开显示
    return false;
end

-- 是否连通的
-- 如果有周围有开启过的正常格子(非障碍、显形格子)
function Grid:isConnected()
    local queue = DungeonM.getAdjoinGrids(self:getPos());
    local list = {};
    local head = 1;

    for _, pos in pairs(queue) do
        list[pos] = true;
    end

    while queue[head] do
        local pos = queue[head];
        local grid = DungeonM.getGridByPos(pos);

        head = head + 1;

        repeat
            -- 门或者已经失效的格子
            if grid.type == GRID_TYPE_DOOR or grid.state == GRID_STATE_DISABLE then
                return true;
            end

            -- 如果未翻开、或者是障碍
            if not grid:isOpened() then
                break;
            elseif grid.type == GRID_TYPE_BLOCK then
                local dbase = DungeonM.query(grid.class, "dbase") or {};
                if dbase.invisible ~= 1 or dbase.steam_machine == 1 then
                    -- 不是隐形障碍
                    break;
                end
            end

            -- 如果是“显形”格子，周围加入队列
            if grid:isPublicGrid() or grid.display then
                local arounds = DungeonM.getAdjoinGrids(pos);
                for _, p in pairs(arounds) do
                    if not list[p] then
                        list[p] = true;
                        table.insert(queue, p);
                    end
                end

                break;
            end

            -- 如果是怪物格子，周围加入队列
            if grid.monster and not grid.monster:isDead() then
                local arounds = DungeonM.getAdjoinGrids(pos);
                for _, p in pairs(arounds) do
                    if not list[p] then
                        list[p] = true;
                        table.insert(queue, p);
                    end
                end

                break;
            end

            -- 周围有空闲的
            return true;
        until true;
    end

    return false;
end

-- 是否能点击
function Grid:canHit()
    -- 已经打开的才能攻击
    if not self:isOpened() then
        return false;
    end

    -- 副本中
    if DungeonInstanceM.isInInstance() then
        return true;
    end

    -- 在boss层
    if DungeonBossM.isInBossLayer() then
        return true;
    end

    -- 下面这些类型的格子需要到达后才能点击
    local specialList = {
        GRID_TYPE_STONE,      -- 石柱
        GRID_TYPE_KRYPTONITE, -- 氪石结界
        GRID_TYPE_TIMEBOMB,   -- 定时炸弹
        GRID_TYPE_GAS_DEVICE, -- 毒气装置
        GRID_TYPE_STEEL_DOOR, -- 钢铁之门
    };

    -- 如果不是远程怪就不管了
    if not self:isPublicGrid() and
       table.indexOf(specialList, self.type) == -1 then
        -- 如果是怪物
        if self.monster and
            (self.monster:isBoss() or #PropM.fetchProps(self.monster, "transfiguration") <= 0) then
            -- 如果被变形了，需要下面的判断
            return true;
        end
    end

    -- 需要判断周围是否都已经开启了
    local pos = self:getPos();
    local arounds = DungeonM.getAdjoinGrids(pos);
    for _, p in pairs(arounds) do
        local g = DungeonM.getGridByPos(p);
        -- 如果旁边是门，直接可以点击
        if g.type == GRID_TYPE_DOOR then
            return true;
        end

        -- 旁边是已翻开的非障碍和非怪物、或者是已经失效的怪旁边
        if g:isOpened() and g.type ~= GRID_TYPE_BLOCK then

            if g:isPublicGrid() or g.display then
                -- 如果是联通的
                if g:isConnected() then
                -- if g.activeState then
                    return true;
                end
            else
                if not g:isMonster() or g.state == GRID_STATE_DISABLE then
                    return true;
                end
            end
            -- -- 如果是公开类格子
            -- if g:isPublicGrid() then
            --     -- 如果是死亡怪物
            --     if g:isMonster() and g.monster:isDead() then
            --         return true;
            --     elseif g.state == GRID_STATE_DISABLE then
            --         return true;
            --     elseif not g:isMonster() then
            --         return true;
            --     end
            -- elseif not g:isMonster() or g.monster:isDead() then
            --     -- 非怪物或者是死亡的怪物
            --     return true;
            -- end
        end
    end

    return false;
end
